#include "shtunelmanager.hxx"
#include "shtunel.hxx"
#include <utility.hxx>
#include "myfilelog.hxx"
#include "loghelper.hxx"
#include "taskmanager.hxx"
#include <macro.hxx>

#include <ctime>

SHTunelManager::SHTunelListUnit::SHTunelListUnit(const string& url1, const string& method1, SHTunelManager* manager1, int size1, int keepInterval1, FileLog* filelog)
:host(url1),
method(method1),
manager(manager1),
size(size1),
keepInterval(keepInterval1),
birth(time(NULL)),
closed(0),
shtunelList(),
shtunelListInUse(),
isShutdownRequested(false),
_filelog(filelog)
{
	//Start();
}

string SHTunelManager::SHTunelListUnit::Signature(void) const
{
	return Signature(host, method);
}
string SHTunelManager::SHTunelListUnit::Signature(const string& host, const string& method)
{
	return host.substr(host.length()-3) + " " + method;;
}
void SHTunelManager::SHTunelListUnit::KeepAlives(void)
{
	Utility::Lock lock(&shtunelListInUse);

	int connected = 0;
	SHTunelList::iterator iter = shtunelList.begin();
	while (iter != shtunelList.end())
	{
		if ((*iter)->KeepAlive())
			++connected;
		else
		{
			(*iter)->Reset();
			++closed;
		}

		++iter;
	}
}

SHTunel* SHTunelManager::SHTunelListUnit::GetSHTunel(void)
{
	Utility::Lock lock(&shtunelListInUse);

	SHTunel* found = NULL;
	if (iterSHTunelList == shtunelList.end())
		iterSHTunelList = shtunelList.begin();

	while (!found && iterSHTunelList != shtunelList.end())
	{
		if ((*iterSHTunelList)->SetInUse())
			found = *iterSHTunelList;

		++iterSHTunelList;
	}

	return found;
}

void SHTunelManager::SHTunelListUnit::Execute(Thread::Arg)
{
	LOGINFO("SHTunelListUnit "<<Signature()<<" starting...");

	{
		Utility::Lock lock(&shtunelListInUse);
		for (int i=0; i<size; i++)
		{
			shtunelList.push_back(new SHTunel(host, method, _filelog));
			iterSHTunelList = shtunelList.begin();
		}
	}

	LOGINFO("SHTunelListUnit "<<Signature()<<" started.");

	while (!isShutdownRequested)
	{
		for (int i=0; i<keepInterval && !isShutdownRequested; i++)
			Thread::Sleep(1);

		if (!isShutdownRequested)
			KeepAlives();
	}

	LOGINFO("SHTunelListUnit "<<Signature()<<" ending...");
	{
		Utility::Lock lock(&shtunelListInUse);
		SHTunelList::iterator iter = shtunelList.begin();
		while (iter != shtunelList.end())
		{
			delete *iter;
			*iter = NULL;
			++iter;
		}
		shtunelList.clear();
		iterSHTunelList = shtunelList.begin();
	}

	LOGINFO("SHTunelListUnit "<<Signature()<<" ended.");
}

void SHTunelManager::SHTunelListUnit::Shutdown(void)
{
	isShutdownRequested = true;
}

SHTunelManager::SHTunelManager(TaskManager* taskManager1, FileLog* filelog)
: shtunelContainer(),
shtunelContainerInUse(),
taskManager(taskManager1),
_filelog(filelog)
{
}

SHTunelManager::~SHTunelManager()
{
	Cleanup();
}

void SHTunelManager::Register(const string& host, const string& method, int size, int keepInterval)
{
	const string signature = SHTunelListUnit::Signature(host, method);

	LOGDEBUG("SHTunelManager::Register "<<signature<<" with size "<<size);

	Utility::Lock lock(&shtunelContainerInUse);

	if (shtunelContainer.find(signature) != shtunelContainer.end())
	{
		LOGWARNING("SHTunelManager::Register "<<signature<<" already exists.");
		return ;
	}

	SHTunelListUnit* lunit = new SHTunelListUnit(host, method, this, size, keepInterval, _filelog);

	shtunelContainer[signature] = lunit;

	LOGDEBUG("SHTunelManager::Register "<<signature<<" successfully with size "<<size);
}

SHTunel* SHTunelManager::GetSHTunel(const string& host, const string& method)
{
	Utility::Lock lock(&shtunelContainerInUse);

	const string signature = SHTunelListUnit::Signature(host, method);
	SHTnuelContainer::iterator iter = shtunelContainer.find(signature);
	if (iter == shtunelContainer.end())
		return NULL;

	SHTunelListUnit* lunit = iter->second;
	return lunit->GetSHTunel();
}

void SHTunelManager::ReturnSHTunel(SHTunel* shtunel)
{
	if (!shtunel->Reset())
		LOGWARNING("SHTunelManager::ReturnSHTunel failed shtunel->GetCurl() #"<<shtunel->GetCurl());
}

void SHTunelManager::Cleanup(void)
{
	Utility::Lock lock(&shtunelContainerInUse);

	SHTnuelContainer::iterator iter = shtunelContainer.begin();
	while (iter != shtunelContainer.end())
	{
		SHTunelListUnit* lunit = iter->second;
		lunit->Shutdown();
		lunit->Join();
		delete lunit;

		++iter;
	}
	shtunelContainer.clear();
}